/*
 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package javax.naming.ldap;

import javax.naming.NamingException;
import javax.naming.Context;

import java.util.Hashtable;

import com.sun.naming.internal.FactoryEnumeration;
import com.sun.naming.internal.ResourceManager;


/**
 * This abstract class represents a factory for creating LDAPv3 controls.
 * LDAPv3 controls are defined in
 * <A HREF="http://www.ietf.org/rfc/rfc2251.txt">RFC 2251</A>.
 * <p>
 * When a service provider receives a response control, it uses control
 * factories to return the specific/appropriate control class implementation.
 *
 * @author Rosanna Lee
 * @author Scott Seligman
 * @author Vincent Ryan
 * @see Control
 * @since 1.3
 */

public abstract class ControlFactory {

  /**
   * Creates a new instance of a control factory.
   */
  protected ControlFactory() {
  }

  /**
   * Creates a control using this control factory.
   * <p>
   * The factory is used by the service provider to return controls
   * that it reads from the LDAP protocol as specialized control classes.
   * Without this mechanism, the provider would be returning
   * controls that only contained data in BER encoded format.
   * <p>
   * Typically, <tt>ctl</tt> is a "basic" control containing
   * BER encoded data. The factory is used to create a specialized
   * control implementation, usually by decoding the BER encoded data,
   * that provides methods to access that data in a type-safe and friendly
   * manner.
   * <p>
   * For example, a factory might use the BER encoded data in
   * basic control and return an instance of a VirtualListReplyControl.
   * <p>
   * If this factory cannot create a control using the argument supplied,
   * it should return null.
   * A factory should only throw an exception if it is sure that
   * it is the only intended factory and that no other control factories
   * should be tried. This might happen, for example, if the BER data
   * in the control does not match what is expected of a control with
   * the given OID. Since this method throws <tt>NamingException</tt>,
   * any other internally generated exception that should be propagated
   * must be wrapped inside a <tt>NamingException</tt>.
   *
   * @param ctl A non-null control.
   * @return A possibly null Control.
   * @throws NamingException If <tt>ctl</tt> contains invalid data that prevents it from being used
   * to create a control. A factory should only throw an exception if it knows how to produce the
   * control (identified by the OID) but is unable to because of, for example invalid BER data.
   */
  public abstract Control getControlInstance(Control ctl) throws NamingException;

  /**
   * Creates a control using known control factories.
   * <p>
   * The following rule is used to create the control:
   * <ul>
   * <li> Use the control factories specified in
   * the <tt>LdapContext.CONTROL_FACTORIES</tt> property of the
   * environment, and of the provider resource file associated with
   * <tt>ctx</tt>, in that order.
   * The value of this property is a colon-separated list of factory
   * class names that are tried in order, and the first one that succeeds
   * in creating the control is the one used.
   * If none of the factories can be loaded,
   * return <code>ctl</code>.
   * If an exception is encountered while creating the control, the
   * exception is passed up to the caller.
   * </ul>
   * <p>
   * Note that a control factory
   * must be public and must have a public constructor that accepts no arguments.
   * <p>
   *
   * @param ctl The non-null control object containing the OID and BER data.
   * @param ctx The possibly null context in which the control is being created. If null, no such
   * information is available.
   * @param env The possibly null environment of the context. This is used to find the value of the
   * <tt>LdapContext.CONTROL_FACTORIES</tt> property.
   * @return A control object created using <code>ctl</code>; or <code>ctl</code> if a control
   * object cannot be created using the algorithm described above.
   * @throws NamingException if a naming exception was encountered while attempting to create the
   * control object. If one of the factories accessed throws an exception, it is propagated up to
   * the caller. If an error was encountered while loading and instantiating the factory and object
   * classes, the exception is wrapped inside a <tt>NamingException</tt> and then rethrown.
   */
  public static Control getControlInstance(Control ctl, Context ctx,
      Hashtable<?, ?> env)
      throws NamingException {

    // Get object factories list from environment properties or
    // provider resource file.
    FactoryEnumeration factories = ResourceManager.getFactories(
        LdapContext.CONTROL_FACTORIES, env, ctx);

    if (factories == null) {
      return ctl;
    }

    // Try each factory until one succeeds
    Control answer = null;
    ControlFactory factory;
    while (answer == null && factories.hasMore()) {
      factory = (ControlFactory) factories.next();
      answer = factory.getControlInstance(ctl);
    }

    return (answer != null) ? answer : ctl;
  }
}
